﻿using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.Linq;

namespace Lr.Core.Validation
{
    public class ValidationRule<TObject> : IValidationRule<TObject>
        where TObject : IValidatableObject, IValidatable
    {
        /// <summary>
        /// Gets the rule's error message.
        /// </summary>
        public string ErrorMessage { get { return _errorMessage; } }
        private readonly string _errorMessage;

        /// <summary>
        /// Gets the properties this rule applies to.
        /// </summary>
        public IEnumerable<string> Properties { get { return _properties; } }
        private readonly List<string> _properties;

        /// <summary>
        /// The validation method/algorithm used to validate.
        /// </summary>
        protected Func<TObject, bool> ValidationMethod { get { return _validationMethod; } }
        private readonly Func<TObject, bool> _validationMethod;
        
        /// <summary>
        /// Creates a new <see cref="ValidationRule"/> of type <see cref="TObject"/>.
        /// </summary>
        /// <param name="errorMessage">The error message when the rule failed.</param>
        /// <param name="properties">The properties held responsible.</param>
        /// <param name="validationMethod">The validation method/algorithm used to validate.</param>
        public ValidationRule(string errorMessage, IEnumerable<string> properties, Func<TObject, bool> validationMethod)        
        {
            ThrowIf.NullOrEmpty(() => errorMessage);
            ThrowIf.Null(() => validationMethod);

            _errorMessage = errorMessage;
            _validationMethod = validationMethod;

            if (properties == null) {
                _properties = new List<string>();
            } else {
                _properties = properties.ToList();
            }
        }

        /// <summary>
        /// Checks whether the given <paramref name="object"/> passes the rule, or not.
        /// </summary>
        /// <param name="object">The object to check.</param>
        /// <returns>True when the rule is passed, false when not.</returns>
        public bool Validate(TObject @object)
        {
            return ValidationMethod(@object);
        }

        /// <summary>
        /// Returns a <see cref="String"/> that represents the object.
        /// </summary>
        /// <returns>A <see cref="String"/> that represents the object.</returns>
        public override string ToString()
        {
            return this.ToDebugString();
        }
    }
}
